#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include<opencv\cv.h>
#include<opencv/cxcore.h>
#include<iostream>
#include<vector>
#include<math.h>
#include <string>

#define LPTSTR char*


void DisplayErrorBox(LPTSTR lpszFunction);
#include "../NeuralNetwork/neuralnetwork.h"
#define ASSIGNMAX(a,b) a=a>b?a:b;
#include "Debug\sqlite3.h"
#define ASSIGNMIN(a,b) a=a<b?a:b;
#define TEST_CODE
using namespace ICProject;
using namespace std;
using namespace cv;
sqlite3 *db;
char *zErrMsg = 0;
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
    int i;
    for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
  }
namespace abhinav{
	
void recurse(IplImage *newimage,int x,int y,vector<CvPoint> listCoordinates)
{	int ctr=1;
		auto imgData=(unsigned char*)newimage->imageData;
	CvScalar clr;
	int a[3]={x-1,x,x+1};
	int b[3]={y-1,y+1,y+1};
	
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			unsigned char* B=&imgData[(b[j])*newimage->width*newimage->nChannels+a[i]*newimage->nChannels+0];
			if(*B>200)
			{	clr.val[0]=0;
			    cvSet2D(newimage,y/newimage->nChannels,x/(newimage->nChannels),clr);
				listCoordinates.push_back(cvPoint(a[i],b[j]));
				//stackoverflow -->recursion is wayyy too deep
				fprintf(stderr,"iteration %d\n",ctr++);
			recurse(newimage,x,y,listCoordinates);
			return;
			}
		}
	}
}

double PolygonByReactangleArea(vector<CvPoint> curveVector,int rectHeight,int rectWidth)
{

	double  area=0. ;
	int xend=curveVector.back().x;
    int yend=curveVector.back().y;
	

	for(std::vector<CvPoint>::iterator it=curveVector.begin();it!=curveVector.end();++it)
  { 
	int xat=it->x;
	int yat=it->y;

    area+=(xend+xat)*(yend-yat);
	xend=xat;
    yend=yat; 
  }
	double ratio=(area*0.5)/(rectHeight*rectWidth);
	return ratio;

}

	
double polygonArea(vector<CvPoint> curveVector)
{

	double  area=0. ;
	int xend=curveVector.back().x;
    int yend=curveVector.back().y;
	

	for(std::vector<CvPoint>::iterator it=curveVector.begin();it!=curveVector.end();++it)
  { 
	int xat=it->x;
	int yat=it->y;

    area+=(xend+xat)*(yend-yat);
	xend=xat;
    yend=yat; 
  }
	area=area*.5;
 // cout<<area;
  int p=curveVector.size();
  //cout<<"\n"<<"Perimeter= "<<p;
  double c=((p*p))/((4*(CV_PI*CV_PI))*area);
  double r=((p*p))/((4*4)*area);
 // cout<<"\n"<<"Circularity"<<c;
  //cout<<"\n"<<"Rectangularity"<<r;
  return area;
}


double Rectangularity(double area,int p)
{
  double r=((p*p))/((4*4)*area);
  return r;
}

double Circularity(double area,int p)
{
  double c=((p*p))/((4*(CV_PI*CV_PI))*area); 
  return c;
}


CvPoint centroid(vector<CvPoint> curveVector)
{	CvPoint a;
	int gx=0,gy=0,area=0;
	int x2,y2,x1,y1;
	for(std::vector<CvPoint>::iterator it=curveVector.begin();it!=curveVector.end();++it)
	{
		x2=it->x;y2=it->y;
		it=it+1;//last iteration mein this becomes invalid as it points to null
		if(it==curveVector.end())
			break;
		x1=it->x;y1=it->y;
		gy=gy+((y2+y1)*(x2*y1-x1*y2));
		gx=gx+((x2+x1)*(x2*y1-x1*y2));
		area=area+(x2*y1-x1*y2);

	}
	area=(area)/2;
	gx=gx/(area*6);
	gy=gy/(area*6);
	a=cvPoint(gx,gy);
//	cout<<"\n"<<"Centroid "<<gx<<","<<gy;
	return a;
}


vector<int> distanceFromCentroid(vector<CvPoint> curveVector)
{
	CvPoint a=centroid(curveVector);
	int xc=a.x;
	int yc=a.y;
	int tempd;
	vector<int> d;
	//cout<<"Distances form Centroid ";
	for(std::vector<CvPoint>::iterator it=curveVector.begin();it!=curveVector.end();++it)
	{	
		tempd=pow((pow((double(it->x-xc)),2)+pow((double(it->y-yc)),2)),0.5);
		d.push_back(tempd);
		//cout<<tempd<<"\n";
	}
	return d;
}
double disFromCenMean(vector<CvPoint> curveVector)
{
	
	vector<int> distances=distanceFromCentroid(curveVector);
	double d=0.0;
	for (unsigned i=0; i<distances.size(); i++) d=d+distances[i];
	return d;
}


vector<CvPoint> ComplexCoordinate(vector<CvPoint> curveVector)
{
	CvPoint a=centroid(curveVector);
	int xc=a.x;
	int yc=a.y;
	int tempx,tempy;
	vector<CvPoint> d;
	cout<<"Complex Co-ordinates ";
	for(std::vector<CvPoint>::iterator it=curveVector.begin();it!=curveVector.end();++it)
	{	
		tempx=it->x-xc; tempy=it->y-yc;
		d.push_back(cvPoint(tempx,tempy));
		cout<<"\n"<<tempx<<","<<tempy;
	}
	return d;
}

std::vector<CvPoint> getCurveVector(const IplImage* image)
{
	
	vector<vector<CvPoint>> netVector;
		IplImage* copyImage=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
	cvCopy(image,copyImage);
	CvMemStorage* storage=cvCreateMemStorage(0);
	CvSeq* first_contour=NULL;
	int n=cvFindContours(copyImage,storage,&first_contour);
	int total=0;
	int loc=0;
	int n2=0;
	for(;first_contour!=NULL;)
	{
		vector<CvPoint> curveVector;
		for(int j=0;j<first_contour->total;j++)
		{
			curveVector.push_back(*(CvPoint*)cvGetSeqElem(first_contour,j));
		}
		netVector.push_back(curveVector);
		if(total<first_contour->total)
			loc=n2;
		n2=n2+1;
		first_contour=first_contour->h_next;
		
	}
	return netVector[loc];
}

vector<int> convexityvector(const IplImage* image)
{
	
	vector<vector<CvPoint>> netVector;
		IplImage* copyImage=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
	cvCopy(image,copyImage);
	CvMemStorage* storage=cvCreateMemStorage(0);
	CvSeq* first_contour=NULL;
	int n=cvFindContours(copyImage,storage,&first_contour);
	vector<int> convexity;
	for(;first_contour!=NULL;)
	{
		int n= cvCheckContourConvexity(first_contour);
		convexity.push_back(n);
		first_contour=first_contour->h_next;
	}
	return convexity;
}

std::vector<vector<CvPoint>> noofcurves(const IplImage* image)
{
	
	vector<vector<CvPoint>> netVector;
		IplImage* copyImage=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
	cvCopy(image,copyImage);
	CvMemStorage* storage=cvCreateMemStorage(0);
	CvSeq* first_contour=NULL;
	int n=cvFindContours(copyImage,storage,&first_contour);
	int total=0;
	for(;first_contour!=NULL;)
	{
		vector<CvPoint> curveVector;
		for(int j=0;j<first_contour->total;j++)
		{
			curveVector.push_back(*(CvPoint*)cvGetSeqElem(first_contour,j));
		}
		netVector.push_back(curveVector);
		
			
		first_contour=first_contour->h_next;
		
	}
	return netVector;
}

double netPerimeter(const IplImage* image)
{
	
	vector<vector<CvPoint>> netVector;
		IplImage* copyImage=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
	cvCopy(image,copyImage);
	CvMemStorage* storage=cvCreateMemStorage(0);
	CvSeq* first_contour=NULL;
	int n=cvFindContours(copyImage,storage,&first_contour);
	int total=0;
	double d=0.00;
	for(;first_contour!=NULL;)
	{
		vector<CvPoint> curveVector;
		for(int j=0;j<first_contour->total;j++)
		{
			curveVector.push_back(*(CvPoint*)cvGetSeqElem(first_contour,j));
		}
		
		d=d+curveVector.size();
			
		first_contour=first_contour->h_next;
		
	}
	return d;
}


    vector<CvPoint> getCurveVector2(const IplImage* temp)
    {
            vector<CvPoint> curveVectors;
            IplImage* image=cvCreateImage(cvSize(temp->width,temp->height),temp->depth,temp->nChannels);
            cvCopy(temp,image);
            CvPoint* nextPoint=NULL;
            auto imgData=(unsigned char*)image->imageData;
            bool found=false;
            for(auto y=0;y<image->height*image->nChannels;y++)
            {
                    for(auto x=0;x<image->width*image->nChannels;x++)
                    {
                            auto b=imgData[(y)*image->width*image->nChannels+x*image->nChannels];
                            if(b>240)
                                    {
                                            nextPoint=&cvPoint(x,y);
                                            found=true;
                                            break;
                            }
                    }
                    if(found)
                            break;
     
            }
     
            while(nextPoint!=NULL){
                    curveVectors.push_back(*nextPoint);
                   
                    found=false;
                    for(int i=-1;i<2;i++)
                          {  for(int j=-1;j<2;j++)
                                    {
                                            auto b=imgData[(nextPoint->y+i)*image->width*image->nChannels+(nextPoint->x+j)*image->nChannels];
                                            if(b>240)
                                            {
                                                    if(nextPoint->x+j>=image->width||nextPoint->y+i>=image->height||nextPoint->y+i<0||nextPoint->x+j<0)
                                                            continue;
                                                   
     
                                                    nextPoint=&cvPoint(nextPoint->x+j,nextPoint->y+i);
													
                                                    CvScalar clr;
                                                    clr.val[0]=1;
                                                    cvSet2D(image,nextPoint->y,nextPoint->x,clr);
                                                    //assert(imgData[(nextPoint->y)*image->width*image->nChannels+nextPoint->x*image->nChannels]==0);
                                                    found=true;
                                                    break;
													
													
                                            }
					}
											if(found){
												//cout<<nextPoint->x<<","<<nextPoint->y<<"\n";
												break;}
					
                            }
                            if(!found)
                                    nextPoint=NULL;
            }
     
			
            return curveVectors;
    }



CvRect GetMinimumBindingRectangle(const IplImage* image)
    {
		
            vector<CvPoint> curveVector=getCurveVector(image);
			//for(int si=0;si<size;si++)
			//{
			int maxX=curveVector[0].x;
            int maxY=curveVector[0].y;
            int minX=curveVector[0].x;
            int minY=curveVector[0].y;
            for(vector<CvPoint>::iterator ptr=curveVector.begin();ptr!=curveVector.end();ptr++)
            {
                    ASSIGNMAX(maxX,ptr->x);
                    ASSIGNMAX(maxY,ptr->y);
                    ASSIGNMIN(minX,ptr->x);
                    ASSIGNMIN(minY,ptr->y);
            }
    #ifdef TEST_CODE
            IplImage* test_image=cvCreateImage(cvSize(image->width,image->height),image->depth,image->nChannels);
            cvCopy(image,test_image);
            cvDrawRect(test_image,cvPoint(minX,minY),cvPoint(maxX,maxY),cvScalar(255,0,0),3);
            cvNamedWindow("test window");
            cvShowImage("test window",test_image);
           cvWaitKey();
            cvReleaseImage(&test_image);
            cvDestroyWindow("test window");
    #endif
     

            return cvRect(minX,minY,(maxX-minX),(maxY-minY));
			}
			//}


}

vector<double> anglesfromrectangle(vector<CvPoint> curveVector,CvRect rect)
{
	vector<double> anglevector;
	CvPoint cent= abhinav::centroid(curveVector);
	
	double a=rect.x+rect.width-cent.x;
	double b=rect.y-cent.y;
	double c=atan(b/a);
	anglevector.push_back(c);
	
	a=rect.x-cent.x;
	b=rect.y-cent.y;
	c=atan(b/a);
	anglevector.push_back(c);
	
	b=rect.y+rect.width-cent.y;
	c=atan(b/a);
	anglevector.push_back(c);
	
	a=rect.x+rect.width-cent.x;
	b=rect.y+rect.height-cent.y;
	c=atan(b/a);
	anglevector.push_back(c);
	
	return anglevector;
}


int sqlrunner(string sqlcmd){
   
	int rc;
    rc = sqlite3_open("database.db", &db);
    if( rc ){
      fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
      sqlite3_close(db);
      return(1);
	  
    }
	const char *a = sqlcmd.c_str();
	
	rc = sqlite3_exec(db,a, callback, 0, &zErrMsg);
    if( rc!=SQLITE_OK ){
      fprintf(stderr, "SQL error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
    }
    sqlite3_close(db);
 return 0;
}



using namespace abhinav;

void detectVertices(String path)
{
	Mat src=imread(path);
	assert(!src.empty());
	
	Mat gray;
	cvtColor(src,gray,CV_BGR2GRAY);
	Mat bw;
	Canny(gray,bw,0,50,5);
	std::vector<std::vector<Point>> contours;
	cv::findContours(bw.clone(),contours,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);
	cv::drawContours(src,contours,0,Scalar(255,0,0),6);
	imshow("TRY",src);
	cvWaitKey();
	vector<Point> approx;
	
	double a=arcLength(Mat(contours[0]),true);
	 approxPolyDP(Mat(contours[0]), approx, a*0.02, true);
	
	 cout<<approx.size();
}


int main(int argc,char** argv)
{   detectVertices("Release\\D.bmp.bmp");
	
	IplImage* image=cvLoadImage("Release\\D.bmp.bmp");
		
		
	
		
		assert(image!=NULL);
		IplImage* binaryImage=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
		cvCvtColor(image,binaryImage,CV_RGB2GRAY);
		/*IplImage* edges=cvCreateImage(cvGetSize(binaryImage),IPL_DEPTH_8U,1);
		cvCanny(binaryImage,edges,240,200);
		vector<CvPoint> cur=getCurveVector(edges);
		*/
		
//	vector<double> input;
//	NeuralNetwork* obj=new NeuralNetwork(12,3,26,100,1000,0.6,0.8);
//		std::map<std::vector<double>,std::vector<double>> patternCollection;
//		 
//		sqlrunner("create table clfdata(Font varchar(20),Alphabet varchar(20),rectanularity double,cirularity double,polygonbyrect double,angle1 double,angle2 double,angle3 double,angle4 double,curvesize double,noofcurves double,convexity double,disfromcentmean double,netperimeter double)");
//		DIR *dpdf;
//		struct dirent *epdf;
//
//		dpdf = opendir("./Fonts");
//		if (dpdf != NULL){epdf = readdir(dpdf);epdf = readdir(dpdf);}
//		
//		
//while (epdf = readdir(dpdf)){
//	printf(epdf->d_name);
//	/*		DIR *emptycheck;
//		char* f="./Fonts/";
//		char* fdir=epdf->d_name;
//		ostringstream emptycheckpath;
//		emptycheckpath<<f<<fdir;
//		emptycheck=opendir(emptycheckpath.str().c_str());
//		if(emptycheck==NULL) continue;
//		*/
//	try{	
//	for(char alpha='A';alpha<='Z';alpha++)
//	 {
//		ostringstream path; 
//		
//
//		char* pathA="E:\\Projects\\LineDetectionAlgo\\LineDetectionAlgo\\Fonts\\";
//		char* pathB=".bmp";
//		char* slash="\\";
//		char* fontdir=epdf->d_name;
//		char charToProcess=alpha;
//		path<<pathA<<fontdir<<slash<<charToProcess<<pathB;
//		
//		IplImage* image=cvLoadImage(path.str().c_str());
//		
//		
//		assert(image!=NULL);
//
//		IplImage* binaryImage=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
//		cvCvtColor(image,binaryImage,CV_RGB2GRAY);
//		IplImage* edges=cvCreateImage(cvGetSize(binaryImage),IPL_DEPTH_8U,1);
//		
//		cvCanny(binaryImage,edges,240,200);
//		cvNamedWindow("My Window",CV_WINDOW_AUTOSIZE);
//		cvShowImage("My Window",edges);
//		cvNamedWindow("My Window2",CV_WINDOW_AUTOSIZE);
//		cvShowImage("My Window2",image);
//		
//		vector<CvPoint> curveVector=getCurveVector(edges);
//		input=*new vector<double>();
//		double polyarea= polygonArea(curveVector);
//		CvRect rectangle=GetMinimumBindingRectangle(edges);
//		int p=curveVector.size();
//		vector<vector<CvPoint>> curves=noofcurves(edges);
//		vector<double> angles= anglesfromrectangle(curveVector,rectangle);
//		input.push_back(Rectangularity(polyarea,p));
//		input.push_back(Circularity(polyarea,p));
//		input.push_back(PolygonByReactangleArea(curveVector,rectangle.height,rectangle.width));
//		input.push_back(abs(angles[0]));
//		input.push_back(abs(angles[1]));
//		input.push_back(abs(angles[2]));
//		input.push_back(abs(angles[3]));
//		input.push_back(curveVector.size()/300.00);
//		input.push_back(double(curves.size()/8.00));
//		vector<int> convexity=convexityvector(edges);
//		input.push_back(convexity[0]);
//		input.push_back(disFromCenMean(curveVector)/double(rectangle.height*500));
//		input.push_back(netPerimeter(edges)/1500);
//		vector<double> output(26,0);
//		output[charToProcess-65]=1;
//		std::pair<std::vector<double>,std::vector<double>> pattern(input,output);
//		patternCollection.insert(pattern);
//		//inputvector.push_back(input);
//		string abc=string("insert into clfdata values('")+string(fontdir)+"','"+to_string(alpha)+"',"+to_string(Rectangularity(polyarea,p))+string(",")+to_string(Circularity(polyarea,p))+string(",")+to_string(PolygonByReactangleArea(curveVector,rectangle.height,rectangle.width))+string(",")+to_string(abs(angles[0]))+string(",")+to_string(abs(angles[1]))+string(",")+to_string(abs(angles[2]))+string(",")+to_string(abs(angles[3]))+string(",")+to_string(curveVector.size()/300.00)+string(",")+to_string(double(curves.size()/8.00))+string(",")+to_string(convexity[0])+string(",")+to_string(disFromCenMean(curveVector)/double(rectangle.height*500))+string(",")+to_string(netPerimeter(edges)/1500)+string(")");
//		sqlrunner(abc);
//
//
//		
//		}
//	}
//	catch(exception ex){}
//	}
//		
//		obj->StartTraining(patternCollection);
//			
//		auto n=obj->ForwardFeed(input);
//		cout<<"abc";

	
/*	
	
	IplImage* image=cvLoadImage("C:\\Users\\Abhinav\\Dropbox\\All\\C.bmp@102030.bmp");
	assert(image!=NULL);
	//NeuralNetwork* obj=new NeuralNetwork(2,2,1,100,1000,0.6,0.8);
	//std::map<std::vector<double>,std::vector<double>> patternCollection;
	//obj->StartTraining(patternCollection);
	
	//GrayImage
	IplImage* binaryImage=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
	cvCvtColor(image,binaryImage,CV_RGB2GRAY);
	IplImage* edges=cvCreateImage(cvGetSize(binaryImage),IPL_DEPTH_8U,1);
	cvCanny(binaryImage,edges,240,200);
	
	CvMemStorage* storage=cvCreateMemStorage(0);
	CvSeq* lines=0;
  //cvHoughLines2(edges,storage,CV_HOUGH_STANDARD,1,CV_PI/180,240,0,0);
   lines = cvHoughLines2( edges, storage, CV_HOUGH_PROBABILISTIC, 1, 1*CV_PI/180, 20, 0, 10 ); 
   
	//edges : canny edge processed image
	//storage : stores the lines
   //lines=cvHoughLines2(edges,storage,CV_HOUGH_STANDARD,1,CV_PI/180,20,edges->height*0.05,5);
  // auto T=async(&checkEdgesFromCannyImage,edges);
   //vector<CvPoint> edgePoints=T.get();
   if(false)     
   for(int i = 0; i < lines->total; i++ ) 
        { 
            CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i); 
            cvLine( image, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
			fprintf(stdout,"(%d,%d) (%d,%d)\n",line[0].x,line[0].y,line[1].x,line[1].y);
        } 
	
	cvNamedWindow("My Window",CV_WINDOW_AUTOSIZE);
	cvShowImage("My Window",edges);
	cvNamedWindow("My Window2",CV_WINDOW_AUTOSIZE);
	cvShowImage("My Window2",image);
	vector<CvPoint> curveVector=getCurveVector(edges);
	 CvRect rectangle=GetMinimumBindingRectangle(edges);
	 double recarea=rectangle.height*rectangle.width;
	 double polyarea= polygonArea(curveVector);
	 int p=curveVector.size();
	// cout<<"ratio2: "<<recarea/p*p;		//Bekaar Hai
	 cout<<"PolygonByRectanglePeri: "<<((rectangle.height*2)+(rectangle.width*2))/(p)<<"\n";
	 double ratio= PolygonByReactangleArea(curveVector,rectangle.height,rectangle.width);
	 cout<<"PolygonByrectangleArea: "<<1/ratio<<"\n";
	 double rec=Rectangularity(polyarea,p);
	 cout<<"Rectangularity: "<<rec<<"\n";
	 double cir=Circularity(polyarea,p);
	 cout<<"Circularity "<<cir<<"\n";
	 CvPoint a=centroid(curveVector);
	 cout<<"Centroid Ratio(x,y): "<<rectangle.width/a.x<<","<<rectangle.height/a.y;
*/


	 
/*	Waste For Now
	// CvPoint compcord=ComplexCoordinate(curveVector);
	// cout<<"Complex Cordinate Ratio: "<<rectangle.width/compcord.x<<","<<rectangle.height/compcord.y;
	 //polygonArea(curveVector);
	 //centroid(curveVector);
	 //distanceFromCentroid(curveVector);
	 //ComplexCoordinate(curveVector);
	//cvHoughLines(edges,1,CV_PI/180,220,&lines,10);
*/
	
	return 0;
}